#pragma once
#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <json/forwards.h>
#include <json/json.h>
#include <json/value.h>
#include <memory>
#include <string>
#include <vector>
#include "../logger.hpp"


namespace shared {

namespace infra {

    
bool Serialize(const Json::Value& obj, std::string& dst)
{
    // 使用 Json::StreamWriter 序列化前需要先使用 工厂类 Json::StreamWriterBuilder 进行实例化
    Json::StreamWriterBuilder swb;
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

    std::stringstream ss;
    int ret = sw->write(obj, &ss);
    if(ret) {
        std::cerr << "json序列化失败\n";
        return false;
    }
    dst = ss.str();
    return true;
}

bool DeSerialize(const std::string& src, Json::Value& obj)
{
    Json::CharReaderBuilder crb;
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

    std::string error_msg; 
    if(false == cr->parse(src.data(), src.data() + src.size(), &obj, &error_msg)) {
        std::cerr << "json反序列化失败\n";
        return false;
    }
    return true;
}

class ESIndex
{
public:
    ESIndex(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "_doc")
        : client_(client), name_(name), type_(type) 
    {
        Json::Value analysis;
        Json::Value analyzer;
        Json::Value ik;
        Json::Value tokenizer;
        tokenizer["tokenizer"] = "ik_max_word";
        ik["ik"] = tokenizer;
        analyzer["analyzer"] = ik;
        analysis["analysis"] = analyzer;
        index_["settings"] = analysis;

        // std::string test;
        // Serialize(index_, test);
        // std::cout << test << '\n';
    }

    ESIndex& append(const std::string& key, const std::string& type = "text", 
                   const std::string& analyzer = "ik_max_word", bool enabled = true)
    {
        Json::Value fields;
        fields["type"] = type;
        fields["analyzer"] = analyzer;
        if(!enabled) {
            fields["enabled"] = enabled;
        }
		properties_[key] = fields;
		return *this;
    }

    bool create(const std::string& index_id = "default_index_id")
    {
        Json::Value mappings;
        mappings["dynamic"] = true;
        mappings["properities"] = properties_;
        index_["mappings"] = mappings;

        std::string body;
        if(Serialize(index_, body) == false)
        {
            LOG_ERROR("索引序列化失败!");
        }
        // LOG_DEBUG("{}", body);

        // 发起搜索请求
        try {
            cpr::Response resp = client_->index(name_, type_, index_id, body);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("创建ES索引 {} 失败，响应状态码异常: {}", name_, resp.status_code);
                return false;
            }
        } catch (const std::exception& e) {
            LOG_ERROR("创建ES索引 {} 失败: {}", name_, e.what());
            return false;
        }
        return true;
    }

private:
    std::string name_;
    std::string type_;
	Json::Value properties_;
	Json::Value index_;
    std::shared_ptr<elasticlient::Client> client_;
};

class ESInsert
{
public:
    ESInsert(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "_doc")
        : client_(client), name_(name), type_(type)
    {}

    template<typename T>
    ESInsert& append(const std::string& key, const T& val)
    {
        item_[key] = val;
        return *this;
    }

    bool insert(const std::string& id = "")
    {
        std::string body;
        bool ret = Serialize(item_, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败！");
            return false;
        }
        // LOG_DEBUG("{}", body);

        // 发起搜索请求
        try {
            cpr::Response resp = client_->index(name_, type_, id, body);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("新增数据 {} 失败，响应状态码异常: {}", body, resp.status_code);
                return false;
            }
        } catch (const std::exception& e) {
            LOG_ERROR("新增数据 {} 失败: {}", body, e.what());
            return false;
        }
        return true;
    }

private:
    std::string name_;
    std::string type_;
    Json::Value item_;
    std::shared_ptr<elasticlient::Client> client_;
};

class ESRemove 
{
public:
    ESRemove(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "_doc")
        : client_(client), name_(name), type_(type)
    {}

    bool remove(const std::string& id)
    {
        try {
            cpr::Response resp = client_->remove(name_, type_, id);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("删除数据 {} 失败，响应状态码异常: {}", id, resp.status_code);
                return false;
            }
        } catch (const std::exception& e) {
            LOG_ERROR("删除数据 {} 失败: {}", id, e.what());
            return false;
        }
        return true;
    }
private:
    std::string name_;
    std::string type_;
    std::shared_ptr<elasticlient::Client> client_;
};

class ESSearch 
{
public:
    ESSearch(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "_doc")
        : client_(client), name_(name), type_(type)
    {}

    ESSearch& append_must_not_terms(const std::string& key, const std::vector<std::string>& values)
    {
        Json::Value fields;
        for (const auto& val : values) {
            fields[key].append(val);
        }
        Json::Value terms;
        terms["terms"] = fields;
        must_not_.append(terms);
        return *this;
    }
    ESSearch& append_must_terms(const std::string& key, const std::string& value)
    {
        Json::Value fields;
        fields[key] = value;

        Json::Value terms;
        terms["match"] = fields;
        must_.append(terms);
        return *this;
    }
    ESSearch& append_should_match(const std::string& key, const std::string& value)
    {
        Json::Value fields;
        fields[key] = value;

        Json::Value match;
        match["match"] = fields;
        should_.append(match);
        return *this;
    }
    ESSearch& append_must_match(const std::string& key, const std::string& value)
    {
        Json::Value fields;
        fields[key] = value;

        Json::Value match;
        match["match"] = fields;
        must_.append(match);
        return *this;
    }
    Json::Value search()
    {
        Json::Value condition;
        if(!must_not_.empty()) condition["must_not"] = must_not_;
        if(!must_.empty()) condition["must"] = must_;
        if(!should_.empty()) condition["should"] = should_;

        Json::Value query;
        query["bool"] = condition;

        Json::Value root;
        root["query"] = query;

        std::string body;
        bool ret = Serialize(root, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败！");
            return Json::Value();
        }
        // LOG_DEBUG("{}", body);

        cpr::Response resp;
        try {
            resp = client_->search(name_, type_, body);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("检索数据 {} 失败，响应状态码异常: {}", body, resp.status_code);
                return Json::Value();
            }
        } catch (const std::exception& e) {
            LOG_ERROR("检索数据 {} 失败: {}", body, e.what());
            return Json::Value();
        }

        // 需要对响应正文进行反序列化成json容器
        // LOG_DEBUG("检索响应正文: [{}]", resp.text);
        Json::Value json_resp;
        if(DeSerialize(resp.text, json_resp) == false) {
            LOG_ERROR("检索数据 {} 结果反序列化失败", resp.text);
            return Json::Value();
        } 
        return json_resp["hits"]["hits"];
    }
private:
    std::string name_;
    std::string type_;
    Json::Value must_not_;
    Json::Value should_;
    Json::Value must_;
    std::shared_ptr<elasticlient::Client> client_;
};

}}